#pragma once
#include "GameTime.h"
#include "Vector.h"

#include <map>
#include <memory>
#include <vector>

namespace dxroids
{
   // enbable this stuff to see what's going on.
   // nice visual debugging
   enum DebugRenderFlags
   {
      None = 0,
      DrawPosition = 1,
      DrawStrechedRadius = 2,
      All = DrawPosition | DrawStrechedRadius
   };   

   struct RenderContext
   {
      ID2D1RenderTarget * Target;
      ID2D1Brush * PrimaryBrush;
      ID2D1Brush * DebugBrush;
      DebugRenderFlags DebugFlags;      
   };

   class GameObject
   {

   public:

      struct basic_traits
      {      
         float bounding_radius;      
         D2D1_POINT_2F (*translate_point)(float _x, float _y);
      };
         

      GameObject(Vector _pos, float _bounding_radius, float _scale = 1.0f);   
      virtual ~GameObject();

      // two-stage contruction. it's not really for exception handling. it's because i want
      // to make make it explicit that each subclass has to construct its ID2D1PathGeometry
      // maybe rename this CreateGeometry()? 
      void Initialize(DXROIDS_COM_PTR<ID2D1Factory> const & _factory);

      virtual bool IntersectsWith(GameObject const & _rhs) const;
      
      void SetRotationalVelocity(float _v);
      void SetPosition(Vector const & _pos);
      void SetVelocity(Vector const & _v);

      bool IsActive() const;
      void SetIsActive(bool _b);

      bool IsVisible() const;
      void SetIsVisible(bool _b);
            
      float GetScale() const;
      float GetDirection() const;
      float BoundingRadius() const;
      Vector const & GetPosition() const;
      Vector const & GetVelocity() const;      

      virtual void UpdateFrame(GameTime const & _timer, ClippingInformation const & _clip, 
         bool * _wrap_horizontal, bool * _wrap_vertical);

      virtual void Render(RenderContext const & _ctx);
      
   protected:
      
      Vector velocity_;      

      virtual DXROIDS_COM_PTR<ID2D1Geometry> CreateGeometry(DXROIDS_COM_PTR<ID2D1Factory> const & _factory,
         float _scale) = 0;

      typedef std::function<bool (GameTime const &)> callback_function_type;
      struct callback_entry
      {
         int64_t due_time;
         float interval;
         callback_function_type callback;
      };
      typedef std::vector<callback_entry> callback_list_type;

      void RegisterTimedCallback(float _seconds, callback_function_type _func);

      DXROIDS_COM_PTR<ID2D1Geometry> CreateScaledPathGeometry(
         DXROIDS_COM_PTR<ID2D1Factory> const & _factory,
         std::vector<D2D1_POINT_2F> & _points,
         float _scale);

      virtual bool IntersectsWithGeometry(DXROIDS_COM_PTR<ID2D1Geometry> const & _rhs) const;
      
      D2D1::Matrix3x2F GetTransform() const;
      DXROIDS_COM_PTR<ID2D1Geometry> GetTransformedGeometry() const;

   private:

      Vector position_;

      float angle_of_rotation_;      
      float rotational_velocity_;      
      float bounding_radius_;
      float scale_;

      bool is_active_;
      bool is_visible_;
      
      callback_list_type registered_callbacks_;

      DXROIDS_COM_PTR<ID2D1Geometry> geometry_;                 
   };
}

inline float dxroids::GameObject::GetDirection() const
{
   return angle_of_rotation_;
}

inline float dxroids::GameObject::GetScale() const
{
   return scale_;
}

inline dxroids::Vector const & dxroids::GameObject::GetPosition() const
{
   return position_;
}

inline dxroids::Vector const & dxroids::GameObject::GetVelocity() const
{
   return velocity_;
}

inline float dxroids::GameObject::BoundingRadius() const
{
   return bounding_radius_;
}

inline void dxroids::GameObject::SetRotationalVelocity(float _v)
{
   rotational_velocity_ = _v;
}

inline void dxroids::GameObject::SetPosition(Vector const & _pos)
{
   position_ = _pos; 
}

inline void dxroids::GameObject::SetVelocity(Vector const & _v)
{
   velocity_ = _v;
   DxRoidsAssert(velocity_.Magnitude() >= (DXROIDS_SPACE_ROCK_SLOW_VELOCITY * .75));
}

inline bool dxroids::GameObject::IsActive() const
{
   return is_active_;
}
 
inline void dxroids::GameObject::SetIsActive(bool _b)
{
   is_active_ = _b;
}


inline bool dxroids::GameObject::IsVisible() const
{
   return is_visible_;
}
 
inline void dxroids::GameObject::SetIsVisible(bool _b)
{
   is_visible_ = _b;
}